/******************************************************************************
 * 
 * Announce: CSharpKit, Basic algorithms, components and definitions.
 *           Copyright (C) ShenYongchen.
 *           All rights reserved.
 *   Author: 申永辰.郑州 (shenyczz@163.com)
 *  WebSite: http://gitee.com/shenyczz/csharp_csk
 *
 * THIS CODE IS LICENSED UNDER THE MIT LICENSE (MIT).
 * THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 
 * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
 * IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
 * PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
 * 
******************************************************************************/
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace CSharpKit.Threading.Tasks
{
    /// <summary>
    /// 用于简化并行代码
    /// </summary>
    public static class CommonParallel
    {
        static CommonParallel()
        {
            _parallelizeOrder = 64;
            _parallelizeElements = 300;
            _taskScheduler = TaskScheduler.Default;
            _maxDegreeOfParallelism = Environment.ProcessorCount;
        }

        #region MaxDegreeOfParallelism - 最大平行度

        static int _maxDegreeOfParallelism;
        /// <summary>
        /// 当并行化适用时应使用多少个并行工作线程（最大平行度）
        /// </summary>
        /// <remarks>
        /// Default to the number of processor cores, must be between 1 and 1024 (inclusive).
        /// </remarks>
        public static int MaxDegreeOfParallelism
        {
            get { return _maxDegreeOfParallelism; }
            set { _maxDegreeOfParallelism = System.Math.Max(1, System.Math.Min(1024, value)); }
        }

        #endregion

        #region TaskScheduler - 任务计划

        private static TaskScheduler _taskScheduler;
        public static TaskScheduler TaskScheduler
        {
            get => _taskScheduler;
            set => _taskScheduler = value;
        }

        #endregion

        #region ParallelizeOrder - 并行顺序

        static int _parallelizeOrder;
        /// <summary>
        /// Gets or sets the order of the matrix when linear algebra provider must calculate multiply in parallel threads.
        /// 当线性代数提供者必须计算并行线程中的乘法时，矩阵的顺序。
        /// </summary>
        /// <value>
        /// The order. Default 64, must be at least 3.
        /// </value>
        public static int ParallelizeOrder
        {
            get { return _parallelizeOrder; }
            set { _parallelizeOrder = System.Math.Max(3, value); }
        }

        #endregion

        #region ParallelizeElements - 并行要素

        static int _parallelizeElements;
        /// <summary>
        /// Gets or sets the number of elements a vector or matrix must contain before we multiply threads.
        /// 在多线程前，获取或设置向量或矩阵必须包含的元素数。
        /// </summary>
        /// <value>Number of elements. Default 300, must be at least 3.</value>
        public static int ParallelizeElements
        {
            get { return _parallelizeElements; }
            set { _parallelizeElements = System.Math.Max(3, value); }
        }

        #endregion

        /// <summary>
        /// 执行一个for循环，其中迭代可以并行运行
        /// Executes a for loop in which iterations may run in parallel.
        /// </summary>
        /// <param name="fromInclusive">The start index, inclusive.</param>
        /// <param name="toExclusive">The end index, exclusive.</param>
        /// <param name="body">The body to be invoked for each iteration range.</param>
        public static void For(int fromInclusive, int toExclusive, Action<int, int> body)
        {
            int rangeSize = System.Math.Max(1, (toExclusive - fromInclusive) / MaxDegreeOfParallelism);
            For(fromInclusive, toExclusive, rangeSize, body);
        }

        /// <summary>
        /// Executes a for loop in which iterations may run in parallel.
        /// </summary>
        /// <param name="fromInclusive">The start index, inclusive.</param>
        /// <param name="toExclusive">The end index, exclusive.</param>
        /// <param name="rangeSize">The partition size for splitting work into smaller pieces.（分割成更小块的分区大小。）</param>
        /// <param name="body">The body to be invoked for each iteration range.</param>
        public static void For(int fromInclusive, int toExclusive, int rangeSize, Action<int, int> body)
        {
            if (body == null)
            {
                throw new ArgumentNullException(nameof(body));
            }

            if (fromInclusive < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(fromInclusive));
            }

            if (fromInclusive > toExclusive)
            {
                throw new ArgumentOutOfRangeException(nameof(toExclusive));
            }

            if (rangeSize < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(rangeSize));
            }

            var length = toExclusive - fromInclusive;

            // Special case: nothing to do
            if (length <= 0)
            {
                return;
            }

            // Special case: not worth to parallelize, inline
            // 不值得并行化处理
            if (MaxDegreeOfParallelism < 2 || (rangeSize * 2) > length)
            {
                body(fromInclusive, toExclusive);
                return;
            }

            // Common case
            Parallel.ForEach(
                Partitioner.Create(fromInclusive, toExclusive, rangeSize),
                CreateParallelOptions(),
                range => body(range.Item1, range.Item2));
        }


        /// <summary>
        /// Executes each of the provided actions inside a discrete, asynchronous task.
        /// </summary>
        /// <param name="actions">An array of actions to execute.</param>
        /// <exception cref="ArgumentException">The actions array contains a <c>null</c> element.</exception>
        /// <exception cref="AggregateException">At least one invocation of the actions threw an exception.</exception>
        public static void Invoke(params Action[] actions)
        {
            // Special case: no action
            if (actions.Length == 0)
            {
                return;
            }

            // Special case: single action, inline
            if (actions.Length == 1)
            {
                actions[0]();
                return;
            }

            // Special case: straight execution without parallelism
            if (MaxDegreeOfParallelism < 2)
            {
                for (int i = 0; i < actions.Length; i++)
                {
                    actions[i]();
                }

                return;
            }

            // Common case
            Parallel.Invoke(CreateParallelOptions(), actions);
        }


        /// <summary>
        /// Parallel 类的方法的操作的选项
        /// </summary>
        /// <returns></returns>
        private static ParallelOptions CreateParallelOptions()
        {
            return new ParallelOptions
            {
                MaxDegreeOfParallelism = _maxDegreeOfParallelism,
                TaskScheduler = _taskScheduler,
            };
        }

        //}}@@@
    }


}
